﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.IO;

namespace SourceConverter.Components {
    [DebuggerDisplay("{FullyQualifiedName}")]
    public class ExtClass {
        public string SourceFile;

        public string Name;
        public string Namespace;
        public string SuperClass;
        public bool Singleton;
        public ExtDescription Description;
        public ExtConstructorCollection Constructors;
        public ExtPropertyCollection Properties;
        public ExtMethodCollection Methods;
        public ExtEventCollection Events;
        public ExtConfigCollection Configs;

        public string FullyQualifiedName { get { return String.IsNullOrEmpty(Namespace) ? Name : Namespace + "." + Name; } }

        public string ToExtSharp() {
            string newName = ExtType.ParseClassName(Name);
            if (newName != Name) {
                SourceConverter.RenamedClasses.Add(FullyQualifiedName, FullyQualifiedName.Replace(Name, newName));
            }

            // undocumented superclass
            if (Name == "XTemplate") SuperClass = "Ext.Template";

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.DHTML;");
            sb.AppendLine();

            sb.AppendLine("namespace " + (String.IsNullOrEmpty(Namespace) ? "Ext" : Namespace) + " {");

            if (SourceConverter.ShowDoc) sb.AppendLine(String.Format("    /// <summary>{0}</summary>", Description.GetDocComment("    ")));
            sb.AppendLine(String.Format("    /// <jssource>{0}</jssource>", SourceFile));
            sb.AppendLine("    [Imported]");
            if (String.IsNullOrEmpty(Namespace)) sb.AppendLine("    [IgnoreNamespace]");
            sb.Append(String.Format("    public class {0} ", newName));
            if (!String.IsNullOrEmpty(SuperClass)) sb.Append(": " + ExtType.ParseType(SuperClass));
            sb.AppendLine(" {");
            sb.AppendLine();

            sb.Append(Constructors.ToExtSharp());
            sb.AppendLine();

            sb.AppendLine("        /// <summary>The reference to the constructor function</summary>");
            sb.AppendLine("        [IntrinsicProperty]");
            sb.AppendLine("        public static Delegate constructor { get { return null; } set { } }");
            sb.AppendLine();

            sb.AppendLine("        /// <summary>The reference to the class that this class inherits from</summary>");
            sb.AppendLine("        [IntrinsicProperty]");
            sb.AppendLine("        public static object superclass { get { return null; } set { } }");
            sb.AppendLine();

            sb.Append(Properties.ToExtSharp());
            sb.AppendLine();

            sb.Append(Methods.ToExtSharp());
            sb.AppendLine();

            sb.Append(SourceConverter.GetMissingCode(FullyQualifiedName));
            sb.AppendLine();

            sb.AppendLine("    }"); // class
            sb.Append(Configs.ToExtSharp(this));
            sb.AppendLine();
            sb.Append(Events.ToExtSharp(Name));
            sb.AppendLine();
            sb.Append(Events.Delegates(Name));
            sb.AppendLine("}"); // namespace
            return sb.ToString();
        }
        
        public void ConvertToExtSharp(ExtClass ec, ExtClassCollection classes) {
            // split methods with varying args
            ExtMethodCollection emc = new ExtMethodCollection();
            emc.AddRangeAndSplitParams(Methods);
            Methods = emc;

            // create overloads with less args
            Methods = Methods.CreateOverloads();

            // split ctors with varying args
            ExtConstructorCollection ctors = new ExtConstructorCollection();
            foreach (ExtConstructor ect in Constructors) ctors.AddRange(ect.SplitByParams());
            Constructors = ctors;
            
            // create overloads with less args for constructors
            Constructors = Constructors.CreateOverloads();
            
            // copy constructors from base type
            Constructors.AddBaseOverloads(ec, classes);

            // add a default constructor with no args
            if (!Constructors.HasDefaultConstructor) Constructors.AddDefault(Name);
        }

        public string ToExtSense() {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(Constructors.ToExtSense(this, Properties));
            sb.AppendLine(String.Format("{0}.prototype = {{", FullyQualifiedName));
            sb.AppendLine(Methods.ToExtSense());
            sb.AppendLine("}");
            sb.AppendLine(String.Format("{0}.__class = true;", FullyQualifiedName));
            sb.AppendLine(String.Format("{0}.__type = \"{0}\";", FullyQualifiedName));
            if (!String.IsNullOrEmpty(SuperClass))
                sb.AppendLine(String.Format("{0}.__baseType = {1};", FullyQualifiedName, SuperClass));
            return sb.ToString();
        }

        public void SaveToDisk(string basepath) {

            // ignore this deprecated class
            if (Namespace == "DragDropMgr") return;

            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "output");
            if (!String.IsNullOrEmpty(basepath)) path = basepath;

            if (!Directory.Exists(path)) Directory.CreateDirectory(path);

            if (!String.IsNullOrEmpty(Namespace)) {
                string[] parts = Namespace.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string dir in parts) {
                    string d = dir.Trim();
                    if (String.IsNullOrEmpty(d)) continue;
                    if (d == "Ext") continue;

                    path = Path.Combine(path, d);
                    if (!Directory.Exists(path)) Directory.CreateDirectory(path);
                }
            }

            path = Path.Combine(path, Name + ".cs");
            Console.WriteLine(path);

            string code = ToExtSharp();
#if DEBUG
            // do not write files while debugging because Visual Studio will complain about changed files
            return;
#else
            File.WriteAllText(path, code);
#endif

        }

        public static void ParseClass(string member, ref ExtClass ec) {
            /**
             * @class Ext.SplitButton
             * @extends Ext.Button
             * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
             * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
             * options to the primary button action, but any custom handler can provide the arrowclick implementation.
             * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
             * @cfg {String} arrowTooltip The title attribute of the arrow
             * @constructor
             * Create a new menu button
             * @param {String/HTMLElement/Element} renderTo The element to append the button to
             * @param {Object} config The config object
             */
            string[] data = member.Split(SourceConverter.CRLFA, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < data.Length; i++) {
                string line = data[i].Trim();
                if (line.StartsWith("@class")) {
                    ec.Name = line.Substring("@class".Length).Trim();
                    if (ec.Name.Contains(".")) {
                        int p = ec.Name.LastIndexOf('.');
                        ec.Namespace = ec.Name.Substring(0, p);
                        ec.Name = ec.Name.Substring(p + 1);
                    }
                } else if (line.StartsWith("@extends")) {
                    ec.SuperClass = line.Substring("@extends".Length).Trim();
                } else if (line.StartsWith("@singleton")) {
                    ec.Singleton = true;
                } else if (line.StartsWith("@constructor")) {
                    string ctor = String.Empty;
                    while (++i < data.Length - 1) {
                        ctor += data[i] + SourceConverter.CRLF;
                    }
                    ExtConstructor.ParseConstructor(ctor, ref ec);
                } else {
                    ec.Description.Value += line + SourceConverter.CRLF;
                }
            }
            ec.Description.Value.Trim(SourceConverter.CRLFA);
        }
    }
}